#include	"MaterialLibrary.h"
#include	"Config.h"
#include	<exception>
#include	<fstream>
#include	<sstream>
#include	<iostream>

MaterialLibrary::MaterialLibrary(const Config & config)
	:	m_config(&config)
	,	m_material()
{
}

MaterialLibrary::~MaterialLibrary()
{
	Cleanup();
}

const std::list< Material > & MaterialLibrary::Materials() const
{
	return m_material;
}

void MaterialLibrary::Load(const std::string & file)
{
	if(m_config->Verbose())
	{
		std::cout << "Processing MTL: " << file << std::endl;
	}
	std::ifstream stream(file.c_str());
	if(stream)
	{
		std::string line;
		while(stream)
		{
			std::getline(stream, line);
			if((stream) && (line.length() != 0) && (line[0] != '#'))
			{
				ParseLine(line);
			}
		}
		stream.close();
	}
	else
	{
		throw std::exception(std::string("Error: Can not open file \"" + file + "\".").c_str());
	}
}

void MaterialLibrary::Cleanup()
{
	m_material.clear();
}

unsigned int MaterialLibrary::MaterialIndex(const std::string & name) const
{
	unsigned int result = 0;
	for(std::list< Material >::const_iterator it = m_material.begin();
		it != m_material.end(); ++it)
	{
		if(name == (*it).name)
		{
			return result;
		}
		++result;
	}
	return unsigned int(-1);
}

void MaterialLibrary::ParseLine(const std::string & line)
{
	std::istringstream stream(line);
	std::string firstWord;
	stream >> firstWord;
	if(firstWord == "newmtl")
	{
		Material mat;
		std::string name;
		stream >> name;
		if(!stream)
		{
			throw std::exception("Error: newmtl command without material name.");
		}
		mat.name = name;
		mat.shininess = 100.0f;
		mat.alpha = 1.0f;
		mat.diffuse.r = 0.8f;
		mat.diffuse.g = 0.8f;
		mat.diffuse.b = 0.8f;
		mat.ambient.r = 0.2f;
		mat.ambient.g = 0.2f;
		mat.ambient.b = 0.2f;
		mat.specular.r = 0.0f;
		mat.specular.g = 0.0f;
		mat.specular.b = 0.0f;
		mat.emission.r = 0.0f;
		mat.emission.g = 0.0f;
		mat.emission.b = 0.0f;
		mat.texture = "";
		m_material.push_back(mat);
	}
	else if(firstWord == "Ns")
	{
		float shininess = 100.0f;
		stream >> shininess;
		if(!stream)
		{
			throw std::exception("Error: shininess command without the actual shininess value.");
		}
		(*m_material.rbegin()).shininess = shininess;
	}
	else if(firstWord == "d")
	{
		float alpha = 1.0f;
		stream >> alpha;
		if(!stream)
		{
			throw std::exception("Error: alpha command without the actual alpha value.");
		}
		(*m_material.rbegin()).alpha = alpha;
	}
	else if(firstWord == "Kd")
	{
		Color diffuse = { 1.0f, 1.0f, 1.0f };
		stream >> diffuse.r;
		if(!stream)
		{
			throw std::exception("Error: diffuse (color) command without the actual color value.");
		}
		stream >> diffuse.g;
		if(!stream)
		{
			diffuse.g = diffuse.r;
			diffuse.b = diffuse.r;
		}
		else
		{
			stream >> diffuse.b;
			if(!stream)
			{
				diffuse.b = diffuse.g;
			}
		}
		(*m_material.rbegin()).diffuse = diffuse;
	}
	else if(firstWord == "Ka")
	{
		Color ambient = { 1.0f, 1.0f, 1.0f };
		stream >> ambient.r;
		if(!stream)
		{
			throw std::exception("Error: ambient (color) command without the actual color value.");
		}
		stream >> ambient.g;
		if(!stream)
		{
			ambient.g = ambient.r;
			ambient.b = ambient.r;
		}
		else
		{
			stream >> ambient.b;
			if(!stream)
			{
				ambient.b = ambient.g;
			}
		}
		(*m_material.rbegin()).ambient = ambient;
	}
	else if(firstWord == "Ks")
	{
		Color specular = { 1.0f, 1.0f, 1.0f };
		stream >> specular.r;
		if(!stream)
		{
			throw std::exception("Error: specular (color) command without the actual color value.");
		}
		stream >> specular.g;
		if(!stream)
		{
			specular.g = specular.r;
			specular.b = specular.r;
		}
		else
		{
			stream >> specular.b;
			if(!stream)
			{
				specular.b = specular.g;
			}
		}
		(*m_material.rbegin()).specular = specular;
	}
	else if(firstWord == "Ke")
	{
		Color emission = { 1.0f, 1.0f, 1.0f };
		stream >> emission.r;
		if(!stream)
		{
			throw std::exception("Error: emission (color) command without the actual color value.");
		}
		stream >> emission.g;
		if(!stream)
		{
			emission.g = emission.r;
			emission.b = emission.r;
		}
		else
		{
			stream >> emission.b;
			if(!stream)
			{
				emission.b = emission.g;
			}
		}
		(*m_material.rbegin()).emission = emission;
	}
	else if(firstWord == "map_Kd")
	{
		std::string theRest;
		std::getline(stream, theRest);
		if(!stream)
		{
			throw std::exception("Error: texture map command without the actual texture name.");
		}
		(*m_material.rbegin()).texture = theRest.substr(theRest.find_first_not_of(" \t"));
	}
	else
	{
		std::cout << "Warning: unsupported command in MTL \"" << firstWord << "\"" << std::endl;
	}
}

